#ifndef CITY_HPP_INCLUDED
#define CITY_HPP_INCLUDED

#include <map>
#include <string>

#include "barter_market.hpp"
#include "building_fwd.hpp"
#include "city_fwd.hpp"
#include "formula_callable.hpp"
#include "government_fwd.hpp"
#include "job.hpp"
#include "merchant.hpp"
#include "money_market.hpp"
#include "project_fwd.hpp"
#include "texture.hpp"
#include "tile.hpp"
#include "tile_logic.hpp"
#include "unit_fwd.hpp"
#include "unit_type_fwd.hpp"
#include "variant.hpp"
#include "wml_node_fwd.hpp"

class world;

class city : public game_logic::formula_callable
{
public:
	city(wml::const_node_ptr node, hex::tile& t, government_ptr gov);
	void new_turn(world& w);
	void end_turn(world& w);
	void draw(const world& w) const;

	const std::string& name() const { return name_; }

	int revenue() const;
	int expenses() const;

	void calculate_influence(world& w);
	void prepare_trades(world& w);
	void execute_trades(world& w);
	const std::map<hex::location, int>& influence() const { return influence_; }

	std::string description() const;

	const money_market& market() const { return market_; }
	money_market& market() { return market_; }
	
	typedef std::map<const_item_type_ptr, int> items_map;
	const items_map& items() const { return items_; }
	void get_items(const_item_type_ptr, int quantity);
	int items_owned(const_item_type_ptr) const;

	// finds how much capacity the city has to store this kind of item.
	int storage_capacity(const_item_type_ptr item) const;

	government_ptr owner();
	const_government_ptr owner() const;

	const std::vector<job_ptr>& jobs() const { return jobs_; }
	void fill_jobs(world& w);
	int add_worker_cost(const world& w, const_job_ptr j, hex::location* loc=NULL) const;
	void hire_worker(world& w, job_ptr j);
	void pay_workers();

	bool recruit_unit(world& w, const_unit_type_ptr u);
	citizen_ptr recruit_citizen_for_unit(world& w, const_unit_type_ptr type);

	struct unit_in_production {
		unit_ptr unit;
		int training_done;
		int training_required;
	};

	const std::vector<unit_in_production>& units_in_production() const { return units_in_production_; }

	bool pay_upkeep_for_unit(const_unit_ptr u);

	void set_selected(bool select) { selected_ = select; }

	int engineering_production() const;

	typedef std::map<const_building_type_ptr, int> buildings_map;
	void add_building(const_building_type_ptr type);
	const buildings_map& buildings() const { return buildings_; }

	void add_project(project_ptr p);
	void remove_project(project_ptr p);
	void work_on_projects(world& w);
	const std::vector<project_ptr>& projects() const { return projects_; }

	hex::tile& tile() { return tile_; }
	const hex::tile& tile() const { return tile_; }

	std::vector<const_unit_type_ptr> can_recruit() const;
	std::vector<const_building_type_ptr> can_build() const;

	int get_shortest_trade_path(const world& w, const const_city_ptr& other,
	                            const std::vector<hex::location>** path) const;

	void add_export(const merchant_trade& trade);
	void add_import(const merchant_trade& trade);

	const std::vector<merchant_trade>& exports() const { return exports_; }
	const std::vector<merchant_trade>& imports() const { return imports_; }

	struct history_entry {
		int population;
		int influence;
		int deaths;
		int births;
		int immigration;
		int emmigration;
	};
	const std::vector<history_entry>& history() const { return history_; }
private:
	void add_history(world& w);
	variant get_value(const std::string& key) const;

	//Makes the city pay an item in upkeep.
	bool pay_upkeep(const_item_type_ptr type);

	government_ptr owner_;
	std::string name_;
	graphics::texture image_;
	std::vector<job_ptr> jobs_;
	hex::tile& tile_;

	//map of locations to distance from city.
	std::map<hex::location, int> influence_;
	money_market market_;

	items_map items_;

	std::vector<project_ptr> projects_;

	buildings_map buildings_;

	mutable int selected_;

	struct trade_route {
		trade_route() : cost(0) {}
		int cost;
		std::vector<hex::location> path;
	};

	mutable std::map<const_city_ptr, trade_route> trade_routes_cache_;
	
	std::vector<merchant_trade> exports_, imports_;

	std::vector<unit_in_production> units_in_production_;

	std::vector<history_entry> history_;
};

#endif
